#include "../../core/graphic/texture.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pspgu.h>
#include <pspgum.h>
#include <psputils.h>

struct PSP_TEXTURE_DESC
{
	int w;
	int h;
	KFormat format;
	bool swizzled;
	bool inEdram;
	void* pTextureData;

	PSP_TEXTURE_DESC() {pTextureData = NULL; swizzled = false; inEdram = false;}
};


KTexture::KTexture()
{
	mTextureData = new PSP_TEXTURE_DESC;
}

KTexture::~KTexture()
{
	Reset();	

	PSP_TEXTURE_DESC* pTex = (PSP_TEXTURE_DESC*)mTextureData;
	delete pTex;
}

// Implementation dependent methods
bool KTexture::CreateFromFile(const char* fileName, bool forceGray)
{
	Reset();

	KBitmap::CreationDesc cdesc;
	cdesc.forceGray = forceGray;
	bool result = KBitmap::LoadFromFile(fileName, this, cdesc);

	if (result) {
		// TODO: swizzle the texture for better performance

	}

	return result;
}

void KTexture::Reset()
{
	PSP_TEXTURE_DESC* pTex = (PSP_TEXTURE_DESC*)mTextureData;
	if (pTex->pTextureData) {
		if (pTex->inEdram) {
			// TODO: support edram texture
		}
		else {
			free(pTex->pTextureData);
		}

	}
}

bool KTexture::CreateFromScratch(int w, int h, KFormat format)
{
	return Allocate(w, h, format);
}

bool KTexture::UpdateRect(int sx, int sy, int w, int h, void* pData)
{
	PSP_TEXTURE_DESC* pTex = (PSP_TEXTURE_DESC*)mTextureData;
	BYTE* pDestData = (BYTE*)pTex->pTextureData;

	if (pTex->format == eRGBA_8888) {
		pDestData += pTex->w * sy * 4;
		pDestData += sx * 4;

		for (int y = 0; y < h; ++y) {
			BYTE* pSrcData = (BYTE*)pData + y * w * 4;

			memcpy(pDestData, pSrcData, w*4);
			pDestData += pTex->w * 4;

		}

	}
	else if (pTex->format == eRGBA_L8) {
		for (int y = 0; y < h; ++y) {
			BYTE* pSrcData = (BYTE*)pData + y * w;

			memcpy(pDestData + sx + (sy+y)*pTex->w, pSrcData, w);
		}

	}
	return true;
}



bool KTexture::Bind()
{
	PSP_TEXTURE_DESC* pTex = (PSP_TEXTURE_DESC*)mTextureData;

	if (pTex->format == eRGBA_8888) {
		sceGuTexMode(GU_PSM_8888,0,0,0);
	}
	else if (pTex->format == eRGBA_L8) {
		sceGuTexMode(GU_PSM_T8,0,0,0);
	}
	else
		return false;

	sceGuTexImage(0,pTex->w,pTex->h,pTex->w,pTex->pTextureData);
	return true;
}

bool KTexture::BeginEdit()
{
	// TODO: Maybe I need to flush GU before access to the texture data
	sceGuTexFlush(); // not sure if I should call  this.
	return true;
}

void KTexture::EndEdit()
{
	// TODO: Maybe I need write back and invalid the texture data
	sceKernelDcacheWritebackInvalidateAll();
}

bool KTexture::Allocate(int w, int h, KFormat format)
{
	PSP_TEXTURE_DESC* pTex = (PSP_TEXTURE_DESC*)mTextureData;
	mWidth = w;
	mHeight = h;
	pTex->w = w;
	pTex->h = h;
	pTex->format = format;
	if (format == eRGBA_8888)
		mBpp = 4;
	else if (format == eRGBA_L8)
		mBpp = 1;

	if (pTex->inEdram) {
		// TODO: Allocate texture from Edram
	}
	else {
		// Allocate texture buffer(need to be 16 byte aligned)

		// http://www.psp-programming.com/forums/index.php?topic=4157.10;wap2
		// From the link above, I know PSP SDK ensures that the memory allocation is 16 byte aligned.
		// 16-byte memory alignment is only guaranteed in the psp sdk, however since texture upload is a very psp specific thing 
		// there shouldn't be cross-platform concerns with it.

		pTex->pTextureData = malloc(w*h * mBpp);
	}
	return true;
}

void* KTexture::GetRowPtr(int row)
{
	PSP_TEXTURE_DESC* pTex = (PSP_TEXTURE_DESC*)mTextureData;
	if (pTex->pTextureData) {
		BYTE* pDataInByte = (BYTE*)pTex->pTextureData;
		return &pDataInByte[row * pTex->w * mBpp];
	}
	else
		return NULL;
}
